public class Monticulo {
	private PriorityQueue<Nodo> monticulo;
	private long width, height;

    /* Evalua la solucion respecto al mapa de la ruta especificada */
	public double val(Camino solucion, String ruta){
		LectorImagenes li = new LectorImagenes();
		int mapa[][] = li.getMapaGS(ruta);
		
		
		double valor=0;
		int x,y;
		for (int i=0;i<solucion.getCamino().size();i++){
			x=(int)solucion.getCamino().get(i).getPos().getX();
			y=(int)solucion.getCamino().get(i).getPos().getY();
			valor+=calcularCoste(solucion.getCamino().get(i).getPieza(),mapa[x][y]);
		}
		return valor;
	}

	/* Restaura el camino cuando hay un corte entre las posiciones cortei y cortef */
	public Camino restaurarCamino(Camino c, Posicion cortei, Posicion cortef, String ruta){
		Camino aux=busqueda(ruta, cortei,cortef, "A*", 0, 0, 0, "euclidea");
		System.out.println(aux.toString());
		int i=0;
		while (!c.getCamino().get(i++).getPos().equals(cortei));
		c.getCamino().remove(i-1);
		/* Nos posicionamos en el nuevo inicio */
		i--;
		for (int j=0;j<aux.getCamino().size();j++,i++){
			aux.getCamino().get(j).setPadre(c.getCamino().get(i-1));
			c.getCamino().add(i,aux.getCamino().get(j));
		}
		c.getCamino().get(i).setPadre(c.getCamino().get(i-1));
		return c;
	}
	
	/* Busca en el camino el primer corte y devuelve la posicion en la lista del ultimo nodo antes del corte */
	public int busquedaNodoCorte(Camino c){
		int inodo=-1;
		for (int i=1; i<c.getCamino().size(); i++)
			if (numeroEnlaces(c, c.getCamino().get(i))==1)
				return i;
		return inodo;
	}
	
	/* Toma la solucion de entrada como ordenada y restaura los caminos cuando encuentra cortes */
	public Camino restaurarSolucion(Camino c, String ruta){
		int inodo=busquedaNodoCorte(c);
		while (inodo!=c.getCamino().size()-1 && inodo!=-1){
			System.out.println(c.getCamino().get(inodo) + " - " + c.getCamino().get(inodo+1));
			c=restaurarCamino(c,c.getCamino().get((int)inodo).getPos(), c.getCamino().get((int)inodo+1).getPos(), ruta);
			inodo=busquedaNodoCorte(c);
		}
		return c;
	}
	
	/* Se devuelve el numero de enlaces que tiene un nodo. dir sirve para indicar la direccion en caso de que solo
	 * tenga un vecino.
	 */
	private int numeroVecinos(Camino solucion, Nodo actual){
		int n=0,dir=0;
		long x,y,xi,yi;
		Nodo inode;
		x=actual.getPos().getX();
		y=actual.getPos().getY();
		for (int i=0;i<solucion.getCamino().size();i++){
			inode=solucion.getCamino().get(i);
			if (!inode.equals(actual)){
				xi=inode.getPos().getX();
				yi=inode.getPos().getY();
				if(x==xi && Math.abs(y-yi)==1){
					n++;
					dir=-1;
				}
				if (Math.abs(x-xi)==1 && y==yi){
					n++;
					dir=1;
				}
			}
		}
		return n*dir;
	}
	
	
	/* Se devuelve el numero de vecinos que tiene un nodo. dir sirve para indicar la direccion en caso de que solo
	 * tenga un vecino.
	 */
	private int numeroEnlaces(Camino solucion, Nodo actual){
		int n=0;
		for (int i=0;i<solucion.getCamino().size();i++){
			if (i==0){
				if(actual.getPadre().equals(solucion.getCamino().get(i))) n++;
			}else{
				if (actual.equals(solucion.getCamino().get(i).getPadre()) || 
					actual.getPadre().equals(solucion.getCamino().get(i)))
					n++;
			}
		}
		return n;
	}
	
	/* Se calcula la pieza que se ha de poner en el nodo a para llegar al b en caso de que
	 * sean vecinos.
	 */
	private int calcularPieza(Nodo a, Nodo b){
		int pieza=0;
		long xa,ya,xb,yb;
		xa=a.getPos().getX();
		ya=a.getPos().getY();
		xb=b.getPos().getX();
		yb=b.getPos().getY();
		
		
		if (xa==xb && Math.abs(ya-yb)==1){
			if(a.getDireccion()==1)
				pieza=3;
			else if (a.getDireccion()==-1)
				pieza=1;
			else
				pieza=-1;
		}
		if (Math.abs(xa-xb)==1 && ya==yb){
			if(a.getDireccion()==1)
				pieza=1;
			else if (a.getDireccion()==-1)
				pieza=3;
			else
				pieza=-1;
		}

		return pieza;
	}
	
	/* Coloca la solucion dada por el conjunto de posiciones desordenadas */
	private Camino colocarSolucion(Camino sol){
		Camino solFinal=new Camino();
		Nodo actual;
		int pieza,dir;
		for (int i=0;i<sol.getCamino().size() && solFinal.getCamino().isEmpty();i++){
			dir=numeroVecinos(sol,sol.getCamino().get(i));
			if(Math.abs(dir)==1){
				sol.getCamino().get(i).setPieza(1);
				sol.getCamino().get(i).setDireccion(dir);
				solFinal.getCamino().add(sol.getCamino().get(i));
				sol.getCamino().remove(i);
			}
		}

		actual=solFinal.getCamino().get(0);

		while(!sol.getCamino().isEmpty()){
			for (int i=0;i<sol.getCamino().size();i++){
				pieza = calcularPieza(actual,sol.getCamino().get(i));
				if (pieza > 0){
					sol.getCamino().get(i).setPadre(actual);
					if (pieza==3) sol.getCamino().get(i).setDireccion(actual.getDireccion()*(-1));
					else sol.getCamino().get(i).setDireccion(actual.getDireccion());
					sol.getCamino().get(i).setPieza(pieza);
					solFinal.getCamino().add(sol.getCamino().get(i));
					actual=sol.getCamino().remove(i);

				}
			}
		}
		
		solFinal.setC(0.0);
		for (int i=0;i<solFinal.getCamino().size();i++)
			solFinal.setC(solFinal.getC()+solFinal.getCamino().get(i).getCoste());
		
		
		solFinal.setM(0.0);
		solFinal.setT(0.0);
		return solFinal;
	}
	
	/* Obtiene una solucion a partir de una imagen con el camino dibujado */
	public Camino getSolucion(String ruta){
		Camino solucion = new Camino();
		LectorImagenes li = new LectorImagenes();
		int mapa[][] = li.getSolucion(ruta);
		int width = li.getImagen().getWidth(null);
		int height = li.getImagen().getHeight(null);

		for (int i=0;i<height;i++)
			for (int j=0;j<width;j++)
				if (mapa[i][j]==1)
					solucion.getCamino().add(new Nodo(new Posicion(i,j)));
		
		
		return colocarSolucion(solucion);
	}
}
